package com.quick.develop.flowable.framework.core;

import cn.hutool.core.map.MapUtil;
import com.quick.develop.flowable.application.IBpmTaskAssigneeApplication;
import com.quick.develop.flowable.constant.BpmConstants;
import com.quick.develop.flowable.enums.BpmExecuteCmd;
import com.quick.develop.flowable.service.definition.IBpmProcessDefinitionService;
import com.quick.develop.flowable.service.process.IBpmEntrustExService;
import com.quick.develop.flowable.service.task.IBpmCommentService;
import com.quick.develop.framework.utils.DateUtils;
import com.quick.develop.framework.utils.StringUtils;
import com.quick.develop.flowable.domain.entrust.BpmEntrustInfoReqVO;
import com.quick.develop.flowable.framework.cmd.RecordTaskInfoChangeCmd;
import com.quick.develop.flowable.framework.factory.FlowServiceFactory;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.engine.delegate.event.impl.FlowableEntityEventImpl;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * @author junyuan.liu
 * @description: 人员分配全局监听、委托用
 * @date 2022/9/6 15:26
 */
@Component
@Slf4j
public class BpmTaskAssignedListener extends FlowServiceFactory implements FlowableEventListener {
    @Resource
    private IBpmEntrustExService bpmEntrustExService;

    @Resource
    private IBpmProcessDefinitionService bpmProcessDefinitionService;

    @Resource
    private IBpmCommentService bpmCommentService;

    @Resource
    private IBpmTaskAssigneeApplication bpmTaskAssigneeApplication;

    @Override
    public void onEvent(FlowableEvent flowableEvent) {

        TaskEntityImpl entity = (TaskEntityImpl) ((FlowableEntityEventImpl) flowableEvent).getEntity();
        Map<String, Object> variables = runtimeService.getVariables(entity.getProcessInstanceId());
        String executeCmd = Optional.ofNullable(variables)
                .map(var -> MapUtil.getStr(var, BpmConstants.EXECUTE_CMD))
                .orElse(null);
        boolean flag = false;
        try {
            // 不需要设置委托的
            if (taskService.hasVariableLocal(entity.getId(), BpmConstants.ENTRUST_IGNORE)) {
                taskService.removeVariableLocal(entity.getId(), BpmConstants.ENTRUST_IGNORE);
                return;
            }
            if (StringUtils.isEmpty(entity.getAssignee()) || Objects.equals(executeCmd, BpmExecuteCmd.START.cmd)) {
                return;
            }

            // 如果是委托释放不再考虑再次委托情况
            if (taskService.hasVariableLocal(entity.getId(), BpmConstants.ENTRUST_RELEASE)) {
                this.releaseRest(entity);
            }
            // 委托
            else {
                this.setEntrustUser(entity);
            }
        } catch (Exception ex) {
            log.error("1009999003-分配任务处理人失败：{}", ex.getMessage());
            flag = true;
            throw ex;
        } finally {
            if (!flag) {
                /* 对所有的设置处理人任务进行保存 / 更新 */
                log.info("1009999004-记录了任务：{}", entity);
            } else {
                log.error("1009999005-发生了异常，任务不记录。");
            }
        }
    }

    private void setEntrustUser(TaskEntityImpl entity) {
        ProcessDefinition processDefinition = bpmProcessDefinitionService
                .getProcessDefinition(entity.getProcessDefinitionId());
        BpmEntrustInfoReqVO reqVO = new BpmEntrustInfoReqVO();
        String taskId = entity.getId();
        reqVO.setProcessInstanceId(entity.getProcessInstanceId());
        reqVO.setCreatedUser(entity.getAssignee());
        reqVO.setModelKey(processDefinition.getKey());
        reqVO.setEntrustDate(DateUtils.dateTime(DateUtils.YYYY_MM_DD, DateUtils.getDate()));
        String entrustUser = bpmEntrustExService.getEntrustUser(reqVO);
        Object transientVariable = entity.getTransientVariable(BpmConstants.AUTO_ENTRUST);
        // 已经委托过了，再次委托人=原来的人  预防死循环
        if (StringUtils.isEmpty(entrustUser) ||
                (Objects.nonNull(transientVariable) && Objects.equals(entrustUser, entity.getOriginalAssignee()))) {
            return;
        }
        if (StringUtils.isNotEmpty(entrustUser) && !Objects.equals(entrustUser, entity.getAssignee())) {
            entity.setTransientVariable(BpmConstants.AUTO_ENTRUST, BpmConstants.AUTO_ENTRUST);
            // 记录最近一次的委托者
            taskService.setVariableLocal(taskId, BpmConstants.ENTRUST_ASSIGNEE, entrustUser);
            // 设置最开始拥有者
            if (!taskService.hasVariableLocal(taskId, BpmConstants.ENTRUST_OWNER)) {
                taskService.setVariableLocal(taskId, BpmConstants.ENTRUST_OWNER, entity.getAssignee());
            }
            this.setAssignee(entity.getAssignee(), entity, entrustUser, 0);
        }
    }

    private void setAssignee(String oldUser, TaskEntityImpl entity, String entrustUser, Integer count) {
        if (count > 3) {
            return;
        }
        boolean flag = false;
        String taskId = entity.getId();
        try {
            taskService.setAssignee(taskId, entrustUser);
            log.info("1009999002-{}将任务：{}，委托给：{}", oldUser, taskId, entrustUser);
            flag = true;
        } catch (Exception e) {
            count = count + 1;
            log.error("1009999001-第{}次委托失败，{}将任务：{}，委托给：{}", count, oldUser, taskId, entrustUser, e);
            setAssignee(oldUser, entity, entrustUser, count);
        } finally {
            if (flag) {
                this.setEntrustFlag(entity);

                String userName = bpmTaskAssigneeApplication.getUserNameByUserCode(oldUser);
                String msg = "【" + userName + "委托】";
                bpmCommentService.addComment(taskId, entity.getProcessInstanceId(), msg, BpmExecuteCmd.AUTO_ENTRUST, null);
            }
        }
    }

    /**
     * 设置委托标识
     */
    private void setEntrustFlag(TaskEntityImpl entity) {
        String description = entity.getDescription();
        if (StringUtils.isNotEmpty(description)) {
            if (description.contains(BpmExecuteCmd.AUTO_ENTRUST.cmd)) {
                return;
            } else if (description.contains(BpmExecuteCmd.RELEASE_ENTRUST.cmd)) {
                description = description.replace(BpmExecuteCmd.RELEASE_ENTRUST.cmd, BpmExecuteCmd.AUTO_ENTRUST.cmd);
            } else {
                description = description + BpmConstants.COMMENT_SEPARATOR + BpmExecuteCmd.AUTO_ENTRUST.cmd;
            }
            entity.setDescription(description);
        } else {
            entity.setDescription(BpmExecuteCmd.AUTO_ENTRUST.cmd);
        }
        managementService.executeCommand(new RecordTaskInfoChangeCmd(entity.getId()));
    }

    /**
     * 委托释放 重置信息
     */
    private void releaseRest(TaskEntityImpl entity) {
        if (taskService.hasVariableLocal(entity.getId(), BpmConstants.ENTRUST_ASSIGNEE)) {
            taskService.removeVariableLocal(entity.getId(), BpmConstants.ENTRUST_ASSIGNEE);
        }
        if (taskService.hasVariableLocal(entity.getId(), BpmConstants.ENTRUST_OWNER)) {
            taskService.removeVariableLocal(entity.getId(), BpmConstants.ENTRUST_OWNER);
        }
        taskService.removeVariableLocal(entity.getId(), BpmConstants.ENTRUST_RELEASE);
        this.restEntrustFlag(entity);
    }

    private void restEntrustFlag(TaskEntityImpl entity) {
        String description = entity.getDescription();
        if (StringUtils.isEmpty(description)) {
            entity.setDescription(BpmExecuteCmd.RELEASE_ENTRUST.cmd);
        } else {
            if (description.contains(BpmExecuteCmd.AUTO_ENTRUST.cmd)) {
                description = description.replace(BpmExecuteCmd.AUTO_ENTRUST.cmd, BpmExecuteCmd.RELEASE_ENTRUST.cmd);
                entity.setDescription(description);
            } else if (!description.contains(BpmExecuteCmd.RELEASE_ENTRUST.cmd)) {
                description = description + BpmConstants.COMMENT_SEPARATOR + BpmExecuteCmd.RELEASE_ENTRUST.cmd;
                entity.setDescription(description);
            }
        }
    }

    @Override
    public boolean isFailOnException() {
        return true;
    }

    @Override
    public boolean isFireOnTransactionLifecycleEvent() {
        return false;
    }

    @Override
    public String getOnTransaction() {
        return null;
    }
}
